﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// translate animation tools, add sustaining or impulse force
/// </summary>
public class ZTranslateAnim : MonoBehaviour {
    
     public AnimationCurve curve = AnimationCurve.Linear(0,0,1,1);
    /// <summary>
    /// 到达判断
    /// </summary>
    public float arrivedCheckDistance = 0.1f;
    
    /// <summary>
    /// 运动总时间（）无外力干扰下
    /// </summary>
    float m_AnimTime;

    /// <summary>
    /// 目标位置
    /// </summary>
    Vector3 m_TargetPos;

    /// <summary>
    /// 平均速度
    /// </summary>
    float m_AverageSpeed;

    /// <summary>
    /// 运动方向
    /// </summary>
    Vector3 m_AnimDir;

   /// <summary>
   /// 开始运动的时间
   /// </summary>
    float curAnimBeginTime;

    const float UseFixedDeltaTime = 1 / 60f;
    float originalFixedDeltaTime = 0.02f;

    public bool moveToTarget = false;

    private Rigidbody m_RigidBody;

    bool m_IsRigidbody;
    public bool isRigidBody
    {
        get
        {
            return  m_IsRigidbody; 
        }
        set
        {
            m_IsRigidbody = value;
            if (value)
            {
                if (!m_RigidBody)
                    m_RigidBody = GetComponentInChildren<Rigidbody>();
            }
        }
    }

    private void Awake()
    {
        m_RigidBody = GetComponentInChildren<Rigidbody>();
        if (m_RigidBody)
            m_IsRigidbody = true;
    }

    // Use this for initialization
    void Start () {

        originalFixedDeltaTime = Time.fixedDeltaTime;
        Time.fixedDeltaTime = UseFixedDeltaTime;
    }

    private void OnDestroy()
    {
        Time.fixedDeltaTime = originalFixedDeltaTime;
    }

    /// <summary>
    /// 在一定时间内移动到目标点
    /// </summary>
    /// <param name="targetPos"></param>
    /// <param name="time"></param>
    public void MoveTo(Vector3 targetPos,float time,Action callback = null)
    {
        moveToTarget = true;
        curAnimBeginTime = Time.realtimeSinceStartup;

        m_AnimTime = time;
        m_TargetPos = targetPos;
        m_AnimDir = targetPos - transform.position;
        m_AverageSpeed = m_AnimDir.magnitude / m_AnimTime;

        DoActionDelay.DelayAction(time,callback);
    }

    /// <summary>
    /// 瞬时力的速度衰减
    /// </summary>
    public float vAtten = 1;

    /// <summary>
    /// 物体质量
    /// </summary>
    float m = 1;


    Velocity Force2Velocity(Force force)
    {
        float t = 1;
        float v = force.power / m * t;//f = ma  v = at

        Velocity newV = new Velocity(force.dir.normalized, v, force.atten);
        return newV;
    }
    [SerializeField]
    List<Velocity> m_VelocityList = new List<Velocity>();
    public List<Velocity> velocityList => m_VelocityList;
    /// <summary>
    /// 增加一个瞬时力
    /// </summary>
    public void AddMomentaryForce(Force force)
    {
        m_VelocityList.Add(Force2Velocity(force));

        //GameDebug.Log($"AddMomentaryForce:{force.dir}  v:{force.power}");
    }

    public void AddVelValueForOneFrame(List<Velocity> forward, float f)
    {
        foreach (Velocity velocity in forward)
        {
            velocity.tempValue = f;
        }
    }

    [SerializeField]
    List<Velocity> m_ContinuesVelocityList = new List<Velocity>();
    public List<Velocity> continuesVelocityList => m_ContinuesVelocityList;
    public void AddContinuesForce(Force force)
    {
        var vel = Force2Velocity(force);
        m_ContinuesVelocityList.Add(vel);
        //Debug.Log($"AddContinuesForce:{force.dir}  v:{vel.v}");
    }
    public void ClearMomentaryVelocity()
    {
        this.velocityList.Clear();
    }
    public void ClearContinuesVelocity()
    {
        this.m_ContinuesVelocityList.Clear();
    }
    public void ClearVelocity()
    {
        ClearMomentaryVelocity();
        ClearContinuesVelocity();
    }

    Queue<Vector3> m_MomentaryMoveVec = new Queue<Vector3>();
    Queue<Vector3> GetForceMoveVector()
    {
        for (int i = 0; i < m_VelocityList.Count; i++)
        {
            Velocity velocity = m_VelocityList[i];

            Vector3 vMove = (velocity.v + velocity.tempValue) * velocity.dir;

            velocity.tempValue = 0;
            velocity.Atten();

            m_MomentaryMoveVec.Enqueue(vMove);
        }
        for (int i = 0; i < m_VelocityList.Count;)
        {
            Velocity velocity = m_VelocityList[i];
            if (velocity.v <= 0)
            {
                m_VelocityList.RemoveAt(i);
            }
            else
            {
                i++;
            }
        }
        return m_MomentaryMoveVec;
    }

    /// <summary>
    /// 获取与目标点的距离
    /// </summary>
    /// <param name="targetPos"></param>
    /// <returns></returns>
    float GetDistance(Vector3 targetPos)
    {
        return (targetPos - transform.position).magnitude;
    }

    float curAnimRealUseTime;

    /// <summary>
    /// 停止运动
    /// </summary>
    void Stop()
    {
        m_VelocityList.Clear();
        m_ContinuesVelocityList.Clear();
        curAnimRealUseTime = Time.realtimeSinceStartup - curAnimBeginTime;
    }

    /// <summary>
    /// 获取向目标点运动的运动距离（每帧）
    /// </summary>
    /// <returns></returns>
    Vector3  GetMoveToTargetMoveVector()
    {
        float elapsedTime = Time.realtimeSinceStartup - curAnimBeginTime;
        float x = elapsedTime / m_AnimTime;

        if (x > 1 - step)
        {
            x = 1 - step;
        }
        //近似求导
        float x2 = x + step;// UseFixedDeltaTime / m_AnimTime;
        float y = this.curve.Evaluate(x);
        float y2 = this.curve.Evaluate(x2);

        var curAverageSpeed = GetDistance(this.m_TargetPos) / (m_AnimTime - elapsedTime);
        m_AverageSpeed = curAverageSpeed > m_AverageSpeed ? curAverageSpeed : m_AverageSpeed;

        float curSpeed = m_AverageSpeed * (y2 - y) / (x2 - x);
        // Debug.Log("x:"+ x+ " x2:"+ x2+ "  y:" + y + " y2: "+ y2+ "  curSpeed:" + curSpeed+ "   Time.fixedDeltaTime:" + Time.fixedDeltaTime);

        Vector3 dir = (m_TargetPos - transform.position).normalized;

        Vector3 moveToTarget = dir * curSpeed * Time.fixedDeltaTime;

        return moveToTarget;
    }


    /// <summary>
    /// delta x
    /// </summary>
    public float step = 0.001f;

	// Update is called once per frame
	void FixedUpdate () {

        if (Time.fixedDeltaTime > UseFixedDeltaTime + 0.0001f)//避免误差
        {
            curAnimBeginTime = Time.realtimeSinceStartup;
            return;
        }

        ///趋向目标点的运动
        if (moveToTarget)
        {
            Vector3 moveToTarget = GetMoveToTargetMoveVector();
            float distance = GetDistance(m_TargetPos);
            if (distance <= arrivedCheckDistance || distance <= moveToTarget.sqrMagnitude)
            {
                //到达停止
                if (isRigidBody)
                    m_RigidBody.position = m_TargetPos;
                else
                    transform.position = m_TargetPos;

                Stop();
                GameDebug.Log($"arrived! m_TargetPos:{m_TargetPos}  GetDistance(m_TargetPos):" + GetDistance(m_TargetPos)); 
                
                this.moveToTarget = false;
                return;
            }

            if (isRigidBody)
                m_RigidBody.position += moveToTarget;
            else
            {
                if (m_RigidBody)
                {
                    if (moveToTarget.y < 0)
                        moveToTarget.y = 0;
                }
                transform.position += moveToTarget;
            }

        }

        Vector3 vec = Vector3.zero;

        var forceMove = GetForceMoveVector();

        if (forceMove.Count == 0 && m_ContinuesVelocityList.Count == 0)
        {
            return;
        }
        //GameDebug.Log($"move force count:{forceMove.Count}");

        //持续力的运动
        foreach (var continues in m_ContinuesVelocityList)
        {
            vec += continues.dir * continues.v;
        }

        //瞬时力的运动
        while (forceMove.Count > 0)
        {
            vec += forceMove.Dequeue();
        }
        //Debug.Log($"translate move:{vec}");
        if (isRigidBody)
        {
            if (vec.y > 0.00001f)
            {
                //GameDebug.Log($"translate move:{vec.y} > 0");
                this.m_RigidBody.velocity = vec;
            }
            else
            {
                if(m_RigidBody == null)
                {
                    GameDebug.LogError($"rigidbody null :{name}");
                    return;
                }
                vec.y += this.m_RigidBody.velocity.y;
                this.m_RigidBody.velocity = vec;
            }
        }
        else
        {
            transform.position += vec * Time.fixedDeltaTime;
        }
    }
}


public class Force
{
    public Vector3 dir;
    public float power;
    public float atten;
    public Force(Vector3 dir, float power)
    {
        this.dir = dir;
        this.power = power;
        this.atten = 1;
    }
    public Force(Vector3 dir, float power, float atten)
    {
        this.dir = dir;
        this.power = power;
        this.atten = atten;
    }
}

[System.Serializable]
public class Velocity
{
    public Vector3 dir;
    public float v;
    public float atten;

    private float m_OriginalV;
    public float originalV => m_OriginalV;

    public float tempValue { get; set; }

    public Velocity(Vector3 dir, float v)
    {
        this.dir = dir.normalized;
        this.v = v;
        this.atten = 1;
    }

    public Velocity(Vector3 dir, float v, float atten)
    {
        this.dir = dir;
        this.v = v;
        this.atten = atten;
        m_OriginalV = v;
    }

    public void Atten()
    {
        v -= atten;
    }
}